package com.kang.c0c.optimize;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import com.kang.c0c.symbol.NameTable;
import com.kang.c0c.symbol.Symbol;
import com.kang.c0c.tree.node.BlockNode;
import com.kang.c0c.tree.node.FunctionNode;
import com.kang.c0c.tree.node.SimpleBlock;

public class Map{
	private Hashtable<Symbol,HashSet<Symbol>> link;
	private Hashtable<Symbol,HashSet<Symbol>> backup;
	private Vector<Symbol> vector;
	private Vector<Symbol> inverse;
	
	public Map(){
		link = new Hashtable<Symbol,HashSet<Symbol>>();
		vector = new Vector<Symbol>();		
	}
	
	public void makeMap(FunctionNode funcNode){
		BlockNode block = funcNode.getBlock();
		int i = 0, size = block.size();
		
		while(i < size){
			SimpleBlock simple = (SimpleBlock)block.get(i);
			Iterator<Symbol> syms = simple.data.getOut().iterator();
			while(syms.hasNext()){
				Symbol sym =syms.next();
				Iterator<Symbol> symsT = simple.data.getOut().iterator();
				while(symsT.hasNext()){
					Symbol symT = symsT.next();
					add(sym, symT);
				}
			}
			i++;
		}
	}
	
	public void initColorUp(){
		backup = new Hashtable<Symbol,HashSet<Symbol>>();
		inverse = new Vector<Symbol>();
		
		int i = 0, size = vector.size();
		
		while(i<size){
			Symbol sym = vector.elementAt(i);
			HashSet<Symbol> set = new HashSet<Symbol>(link.get(sym));
			backup.put(sym, set);
			
			i++;
		}
	}
	
	public void colorUp(NameTable names, int max){
		makeInverse(max);
		
		int index = inverse.size() - 1;
		while(index >= 0){
			Symbol sym = inverse.elementAt(index);
			count(sym);
			index--;
		}
	}
	
	private void add(Symbol sym1, Symbol sym2){
		HashSet<Symbol> set = link.get(sym1);
		
		if(set == null){
			set = new HashSet<Symbol>();
			vector.add(sym1);
			link.put(sym1, set);				
		}
		
		if(sym1 != sym2)
			set.add(sym2);
	}
	
	private void makeInverse(int max){
		while(! vector.isEmpty()){
			int index = 0, size = vector.size();
			
			while(index < size){
				Symbol sym = vector.elementAt(index);
				HashSet<Symbol> set = link.get(sym);
				if(set.size() < max)
					break;
				
				index ++;
			}
			
			if(index >= size){
				Symbol sym = vector.elementAt(0);
				removeVector(sym);
				removeBackup(sym);
			}else{
				Symbol sym = vector.elementAt(index);
				inverse(sym);
			}
		}
	}
	
	private void removeVector(Symbol sym){
		vector.remove(sym);
		
		Iterator<Symbol> iterator = (link.get(sym)).iterator();
		while(iterator.hasNext()){
			Symbol next = iterator.next();
			HashSet<Symbol> set = link.get(next);
			if(set != null)
				set.remove(sym);
		}		
	}
	
	private void removeBackup(Symbol sym){
		Iterator<Symbol> iterator = (backup.get(sym)).iterator();
		while(iterator.hasNext()){
			Symbol next = iterator.next();
			HashSet<Symbol> set = backup.get(next);
			if(set != null)
				set.remove(sym);
		}
	}
	
	private void inverse(Symbol sym){
		inverse.add(sym);
		removeVector(sym);
	}
	
	private void count(Symbol sym){
		int counter = 0;
		int colors = 0;
		Iterator<Symbol> iterator = backup.get(sym).iterator();
		
		while(iterator.hasNext()){
			Symbol s = iterator.next();
			if(s.getPos() > 0){
				counter += s.getPos();
				colors ++;
			}
		}
		
		sym.addr = new Address();
		
		switch(counter){
		case 0:
			sym.setPos(1);
			sym.addr.setReg(1);
			break;
		case 1:
			sym.setPos(2);
			sym.addr.setReg(2);
			break;
		case 2:
			sym.setPos(3);
			sym.addr.setReg(3);
			break;
		case 3:
			if(colors == 2){
				sym.setPos(3);
				sym.addr.setReg(3);
			}else{
				sym.setPos(2);
				sym.addr.setReg(2);
			}
			break;
		case 4:
			sym.setPos(2);
			sym.addr.setReg(2);
			break;
		case 5:
			sym.setPos(1);
			sym.addr.setReg(1);
			break;
		default:
			System.err.println("Error:Map.count 143");
			System.exit(-1);
			break;
		}
	}
}